#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <omp.h>

#define TAM_PALAVRA 70
#define TAM_BLOCO 200

int busca_palindromo(char *s);
int verificador(char *s);
int verifica_primo(int valor);
void verifica_bloco(char *bloco, char **palavra, int *num);//parser para arquivo grande

int main (int argc, char *argv[]){
   	int i = 0, num = 0;
   	char bloco[TAM_BLOCO], *palavra[TAM_PALAVRA];
   	int contPa = 0, contPr = 0, valor = 0;

   	FILE *arq_grand;

   	arq_grand = fopen("wikipedia.txt","r");

   	if (arq_grand != NULL){//exception
     		while(!feof(arq_grand)){//Lê o arquivo, separa-o em threads(2), e processa-o
               //Comando que separa o processo em 2 threads, com valores privados e valores compatilhados
                #pragma omp parallel num_threads(4) private(i,valor) reduction(+:contPa,contPr) shared(bloco,palavra)

		//Cada thread vai cuidar de uma sessão
                #pragma omp sections
	       		{
	       		    #pragma omp section
	       		   { fgets(bloco,TAM_BLOCO,arq_grand);

                    }

                     #pragma omp section
	       	      {
	       	       verifica_bloco(bloco,palavra,&num);

               for(i = 0;i < num;i++)
	       		 {
				//verifica se as palavras sao palindromos
		 	          valor = verificador(palavra[i]);
			 	    if(valor != 0)
			 	    { 
                       #pragma omp critical
					   contPa++;
					//verifica se sao primos
			     		  if (verifica_primo(valor))
                          #pragma omp critical
						  contPr++;
			 	    }
                  }
              }
            }
            #pragma omp barrier

     		}
     	printf("No de Palindromos: %d\nNo de Palindromos Primos: %d\n",contPa,contPr);
   	}
   fclose(arq_grand);
   //----------------------------------------------
   //----------------------------------------------
   return 0;
}
//--------------------------------------------------
//--------------------------------------------------
//--------------------------------------------------
void verifica_bloco(char *bloco, char **palavra, int *num){//parser para verificar palavra por palavra
	int m = 0;

        char *palavra_aux = strtok(bloco, " ,./?'\";:|^-!$#@`~*&%)(+=_}{][\n\r\t\\");

        while(palavra_aux != NULL){
                palavra[m] = palavra_aux;
                palavra_aux = strtok(NULL, " ,./?'\";:|^-!$#@`~*&%)(+=_}{][\n\r\t\\");
                m++;
        }
        *num = m;
}

/**converte para minusculo se necessário, verifica se já se é palindromo**/
/**retorna o valor total em ascii**/

int verificador(char *s){
   	int i, j = 0, test = 0;
    	char bloco[200];

    	for(i=0; i < strlen(s); i++){
        	if (isupper(s[i])){ /**maiusculo->minusculo**/
            		s[i] += 32;
            		bloco[j++] = s[i];
        	}
        	else if(isdigit(s[i]) || islower(s[i])){ /**verifica se já numeros no meio ou se já é minuscula **/
        		bloco[j++] = s[i];
        	}
        	else
        		bloco[j] = '\0'; //coloca final de string
    	}

    	test = busca_palindromo(bloco);

   	return test;
}

/**verifica se a palavra é palindrmo e já converte para ascii**/
int busca_palindromo(char *s){
   	int i, l = 0, count = 0;
   	l = strlen(s);

   	if (l == 0)
      		return 0;

   	for(i = 0; i < l/2; i++){ /**para palindromo par**/

    		if ( s[i] != s[l-i-1] ) /** se já for diferente o inicio e o fim retorna 0 **/
         		return 0;
       		/**senão vai somando o inicio e o fim da palavra e convertendo para ascii**/
        	count += s[i];
        	count += s[l-i-1];

   	}
   	if(l%2 == 1)/**para palindromo impar soma o termo do meio**/
     		return (count + s[1+l/2]);

   	else
     		return count;
}


int verifica_primo(int valor){
	int primes[15000], /* suficiente para esse programa */
            next = 0, root = 0, total = 0, prime = 0, i = 0;

	primes[0] = total = 2;
	primes[1] = 3;
	next = 5;

	while(next <= valor){
        	root = sqrt(next) + 1;
        	prime = 1;
        	for(i = 0; (primes[i] <= root) && (i < total); i++){
		    	if(next % primes[i] == 0){
		        	prime = 0;
		        	break;
		   	}
       		}

        	if(prime){
            		primes[total] = next;
            		total++;
        	}
        	next += 2;
    	}
    	if(valor == 2 || valor == 3)  //verifica o valor inicial de num primos
        	return 1;
    	else
        	return (primes[total - 1] == valor);
}

